# use __getattr__
# if not have attribute system will call __getattr__ function


class LazyDB(object):
    def __init__(self):
        self.exists = 5

    def __getattr__(self, name):
        value = 'Value for %s' % name
        setattr(self, name, value)
        return value


data = LazyDB()
print('Before:', data.__dict__)
print('foo: ', data.foo)
print('After:', data.__dict__)


class LogginLazyDB(LazyDB):
    def __getattr__(self, name):
        print('Called __getattr__ %s' % name)
        return super().__getattr__(name)


data = LogginLazyDB()
print('Before:', data.__dict__)
print('foo: ', data.foo)
print('foo: ', data.foo)
print('After:', data.__dict__)


data = LogginLazyDB()
print('Before:', data.__dict__)
# True, because has first call __getattr__
print('foo: ', hasattr(data, 'foo'))
print('After:', data.__dict__)
print('foo: ', hasattr(data, 'foo'))


class ValidatingDB(object):
    def __init__(self):
        self.exists = 5

    def __getattribute__(self, name):
        print('Called __getattribute__ (%s)' % name)
        try:
            return super().__getattribute__(name)
        except AttributeError:
            value = 'Value for %s ' % name
            setattr(self, name, value)
            return value


data = ValidatingDB()
print('exists:', data.exists)
print('foo: ', data.foo)
print('foo: ', data.foo)

print('foo: ', hasattr(data, 'foo'))
print('foo: ', hasattr(data, 'foo'))


class SavingDB(object):
    def __setattr__(self, name, value):
        super().__setattr__(name, value)


class LogginSavingDB(SavingDB):
    def __setattr__(self, name, value):
        print('Called __setattr__ (%s, %r)' % (name, value))
        super().__setattr__(name, value)

data = LogginSavingDB()

print('Before:', data.__dict__)
data.foo = 5
print('After:', data.__dict__)
data.foo = 7
print('Finally:', data.__dict__)
